use async_trait::async_trait;
use url::Url;
use crate::error::{DownloadError, DownloadResult};
use crate::format::{SegmentFormat, FormatDetector};
use crate::url_resolver::{UrlResolver, UrlResolverFactory};

/// 视频分片接口
pub trait Segment: Clone + Send + Sync {
    fn url(&self) -> &str;
    fn index(&self) -> usize;
    fn duration(&self) -> f64;
}

/// M3U8 分片实现
#[derive(Debug, Clone)]
pub struct M3u8Segment {
    url: String,
    duration: f64,
    index: usize,
    format_id: String,
    progress_icon: String,
}

impl M3u8Segment {
    pub fn new(url: String, duration: f64, index: usize) -> Self {
        let detector = FormatDetector::new();
        let format = detector.detect_format(&url).unwrap_or_else(|| detector.default_format());
        
        Self { 
            url, 
            duration, 
            index,
            format_id: format.format_id().to_string(),
            progress_icon: format.progress_icon().to_string(),
        }
    }
    
    /// 使用指定格式创建分片
    pub fn with_format(url: String, duration: f64, index: usize, format: &dyn SegmentFormat) -> Self {
        Self {
            url,
            duration,
            index,
            format_id: format.format_id().to_string(),
            progress_icon: format.progress_icon().to_string(),
        }
    }
    
    /// 获取格式ID
    pub fn format_id(&self) -> &str {
        &self.format_id
    }
    
    /// 获取进度条图标
    pub fn progress_icon(&self) -> &str {
        &self.progress_icon
    }
}

impl Segment for M3u8Segment {
    fn url(&self) -> &str {
        &self.url
    }

    fn index(&self) -> usize {
        self.index
    }

    fn duration(&self) -> f64 {
        self.duration
    }
}

/// 播放列表接口
#[async_trait]
pub trait Playlist: Send + Sync + std::fmt::Debug {
    type SegmentType: Segment;
    
    /// 从内容解析播放列表
    fn parse(content: &str, base_url: Url) -> DownloadResult<Self> where Self: Sized;
    
    /// 获取所有分片
    fn segments(&self) -> &[Self::SegmentType];
    
    /// 获取基础URL
    fn base_url(&self) -> &Url;
    
    /// 验证播放列表
    fn validate(&self) -> DownloadResult<()> {
        if self.segments().is_empty() {
            return Err(DownloadError::NoSegments);
        }
        Ok(())
    }
}

/// M3U8 播放列表实现
pub struct M3u8Playlist {
    segments: Vec<M3u8Segment>,
    base_url: Url,
    format_detector: FormatDetector,
    url_resolver: Box<dyn UrlResolver>,
}

impl M3u8Playlist {
    /// 使用默认配置创建
    pub fn new(base_url: Url) -> Self {
        Self {
            segments: Vec::new(),
            base_url,
            format_detector: FormatDetector::new(),
            url_resolver: UrlResolverFactory::create_smart_resolver(),
        }
    }
    
    /// 使用指定URL解析器创建
    pub fn with_url_resolver(base_url: Url, url_resolver: Box<dyn UrlResolver>) -> Self {
        Self {
            segments: Vec::new(),
            base_url,
            format_detector: FormatDetector::new(),
            url_resolver,
        }
    }
}

impl std::fmt::Debug for M3u8Playlist {
    fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
        f.debug_struct("M3u8Playlist")
            .field("segments", &self.segments)
            .field("base_url", &self.base_url)
            .field("format_detector", &"FormatDetector")
            .field("url_resolver", &self.url_resolver.resolver_type())
            .finish()
    }
}

#[async_trait]
impl Playlist for M3u8Playlist {
    type SegmentType = M3u8Segment;

    fn parse(content: &str, base_url: Url) -> DownloadResult<Self> {
        let mut playlist = Self::new(base_url);
        let mut current_duration = 0.0;
        let mut index = 0;
        
        for line in content.lines() {
            let line = line.trim();
            
            // 解析分片时长
            if line.starts_with("#EXTINF:") {
                if let Some(duration_str) = line.strip_prefix("#EXTINF:") {
                    if let Some(comma_pos) = duration_str.find(',') {
                        let duration_part = &duration_str[..comma_pos];
                        current_duration = duration_part.parse().unwrap_or(0.0);
                    }
                }
            }
            // 解析分片 URL (使用格式检测器)
            else if !line.starts_with("#") && !line.is_empty() {
                // 只处理非注释的非空行
                let segment_url = playlist.url_resolver.resolve_url(&playlist.base_url, line)?;
                // 使用完整的URL来检测格式，而不是仅仅使用路径部分
                let format = playlist.format_detector.detect_format(&segment_url).unwrap_or_else(|| playlist.format_detector.default_format());
                
                let segment = M3u8Segment::with_format(segment_url, current_duration, index, format);
                playlist.segments.push(segment);
                index += 1;
                current_duration = 0.0;
            }
        }
        
        playlist.validate()?;
        Ok(playlist)
    }

    fn segments(&self) -> &[Self::SegmentType] {
        &self.segments
    }

    fn base_url(&self) -> &Url {
        &self.base_url
    }
}

/// 播放列表工厂
pub struct PlaylistFactory;

impl PlaylistFactory {
    /// 根据URL或内容类型创建适当的播放列表解析器
    pub fn create_playlist(content: &str, base_url: Url) -> DownloadResult<Box<dyn Playlist<SegmentType = M3u8Segment>>> {
        // 目前只支持 M3U8，未来可以扩展支持其他格式
        if content.contains("#EXTM3U") || content.contains("#EXTINF") {
            let playlist = M3u8Playlist::parse(content, base_url)?;
            Ok(Box::new(playlist))
        } else {
            Err(DownloadError::InvalidM3u8)
        }
    }
}

#[cfg(test)]
mod tests {
    use super::*;
    use url::Url;

    fn create_test_base_url() -> Url {
        Url::parse("https://example.com/videos/").unwrap()
    }

    #[test]
    fn test_m3u8_segment_creation() {
        let segment = M3u8Segment::new(
            "https://example.com/segment1.ts".to_string(),
            10.0,
            0
        );
        
        assert_eq!(segment.url(), "https://example.com/segment1.ts");
        assert_eq!(segment.duration(), 10.0);
        assert_eq!(segment.index(), 0);
    }

    #[test]
    fn test_m3u8_segment_clone() {
        let original = M3u8Segment::new(
            "https://example.com/segment1.ts".to_string(),
            5.5,
            42
        );
        let cloned = original.clone();
        
        assert_eq!(original.url(), cloned.url());
        assert_eq!(original.duration(), cloned.duration());
        assert_eq!(original.index(), cloned.index());
    }

    #[test]
    fn test_m3u8_playlist_parse_valid() {
        let content = "#EXTM3U\n#EXT-X-VERSION:3\n#EXTINF:10.0,\nsegment1.ts\n#EXTINF:5.5,\nsegment2.ts\n#EXT-X-ENDLIST";
        let base_url = create_test_base_url();
        
        let playlist = M3u8Playlist::parse(content, base_url).unwrap();
        let segments = playlist.segments();
        
        assert_eq!(segments.len(), 2);
        
        assert_eq!(segments[0].url(), "https://example.com/videos/segment1.ts");
        assert_eq!(segments[0].duration(), 10.0);
        assert_eq!(segments[0].index(), 0);
        
        assert_eq!(segments[1].url(), "https://example.com/videos/segment2.ts");
        assert_eq!(segments[1].duration(), 5.5);
        assert_eq!(segments[1].index(), 1);
    }

    #[test]
    fn test_m3u8_playlist_parse_absolute_urls() {
        let content = "#EXTM3U\n#EXTINF:8.0,\nhttps://cdn.example.com/segment1.ts\n#EXTINF:12.0,\nhttps://cdn.example.com/segment2.ts";
        let base_url = create_test_base_url();
        
        let playlist = M3u8Playlist::parse(content, base_url).unwrap();
        let segments = playlist.segments();
        
        assert_eq!(segments.len(), 2);
        assert_eq!(segments[0].url(), "https://cdn.example.com/segment1.ts");
        assert_eq!(segments[1].url(), "https://cdn.example.com/segment2.ts");
    }

    #[test]
    fn test_m3u8_playlist_parse_m4s_segments() {
        let content = "#EXTM3U\n#EXTINF:6.0,\nsegment1.m4s\n#EXTINF:4.0,\nsegment2.m4s";
        let base_url = create_test_base_url();
        
        let playlist = M3u8Playlist::parse(content, base_url).unwrap();
        let segments = playlist.segments();
        
        assert_eq!(segments.len(), 2);
        assert!(segments[0].url().ends_with(".m4s"));
        assert!(segments[1].url().ends_with(".m4s"));
    }

    #[test]
    fn test_m3u8_playlist_parse_empty() {
        let content = "#EXTM3U\n#EXT-X-VERSION:3\n#EXT-X-ENDLIST";
        let base_url = create_test_base_url();
        
        let result = M3u8Playlist::parse(content, base_url);
        assert!(result.is_err());
        assert!(matches!(result.unwrap_err(), DownloadError::NoSegments));
    }

    #[test]
    fn test_m3u8_playlist_parse_no_segments() {
        let content = "#EXTM3U\n# 这是一个注释\n#EXT-X-VERSION:3";
        let base_url = create_test_base_url();
        
        let result = M3u8Playlist::parse(content, base_url);
        assert!(result.is_err());
        assert!(matches!(result.unwrap_err(), DownloadError::NoSegments));
    }

    #[test]
    fn test_m3u8_playlist_parse_malformed_extinf() {
        let content = "#EXTM3U\n#EXTINF:invalid,\nsegment1.ts";
        let base_url = create_test_base_url();
        
        let playlist = M3u8Playlist::parse(content, base_url).unwrap();
        let segments = playlist.segments();
        
        // 应该仍然解析成功，但持续时间为0.0
        assert_eq!(segments.len(), 1);
        assert_eq!(segments[0].duration(), 0.0);
    }

    #[test]
    fn test_m3u8_playlist_parse_missing_comma() {
        let content = "#EXTM3U\n#EXTINF:10.0\nsegment1.ts";
        let base_url = create_test_base_url();
        
        let playlist = M3u8Playlist::parse(content, base_url).unwrap();
        let segments = playlist.segments();
        
        // 应该解析成功，但持续时间可能不正确
        assert_eq!(segments.len(), 1);
    }

    #[test]
    fn test_m3u8_playlist_parse_complex() {
        let content = "#EXTM3U
#EXT-X-VERSION:3
#EXT-X-TARGETDURATION:10
#EXTINF:9.9,
segment1.ts
#EXTINF:9.9,
segment2.ts
#EXTINF:3.0,
segment3.ts
#EXT-X-ENDLIST";
        let base_url = create_test_base_url();
        
        let playlist = M3u8Playlist::parse(content, base_url).unwrap();
        let segments = playlist.segments();
        
        assert_eq!(segments.len(), 3);
        assert_eq!(segments[0].duration(), 9.9);
        assert_eq!(segments[1].duration(), 9.9);
        assert_eq!(segments[2].duration(), 3.0);
        
        // 验证索引递增
        assert_eq!(segments[0].index(), 0);
        assert_eq!(segments[1].index(), 1);
        assert_eq!(segments[2].index(), 2);
    }

    #[test]
    fn test_m3u8_playlist_base_url() {
        let content = "#EXTM3U\n#EXTINF:10.0,\nsegment1.ts";
        let base_url = create_test_base_url();
        
        let playlist = M3u8Playlist::parse(content, base_url.clone()).unwrap();
        
        assert_eq!(playlist.base_url(), &base_url);
    }

    #[test]
    fn test_m3u8_playlist_validate() {
        let content = "#EXTM3U\n#EXTINF:10.0,\nsegment1.ts";
        let base_url = create_test_base_url();
        
        let playlist = M3u8Playlist::parse(content, base_url).unwrap();
        
        // validate 方法在 parse 中已经被调用，所以这里主要测试直接调用
        let validation_result = playlist.validate();
        assert!(validation_result.is_ok());
    }

    #[test]
    fn test_playlist_factory_create_m3u8() {
        let content = "#EXTM3U\n#EXTINF:10.0,\nsegment1.ts";
        let base_url = create_test_base_url();
        
        let playlist = PlaylistFactory::create_playlist(content, base_url).unwrap();
        
        assert_eq!(playlist.segments().len(), 1);
    }

    #[test]
    fn test_playlist_factory_create_extinf_only() {
        let content = "#EXTINF:5.0,\nsegment1.ts\n#EXTINF:8.0,\nsegment2.ts";
        let base_url = create_test_base_url();
        
        let playlist = PlaylistFactory::create_playlist(content, base_url).unwrap();
        
        assert_eq!(playlist.segments().len(), 2);
    }

    #[test]
    fn test_playlist_factory_invalid_format() {
        let content = "This is not a valid playlist\nJust some random text";
        let base_url = create_test_base_url();
        
        let result = PlaylistFactory::create_playlist(content, base_url);
        assert!(result.is_err());
        assert!(matches!(result.unwrap_err(), DownloadError::InvalidM3u8));
    }

    #[test]
    fn test_playlist_factory_empty_content() {
        let content = "";
        let base_url = create_test_base_url();
        
        let result = PlaylistFactory::create_playlist(content, base_url);
        assert!(result.is_err());
    }

    #[test]
    fn test_m3u8_playlist_debug() {
        let content = "#EXTM3U\n#EXTINF:10.0,\nsegment1.ts";
        let base_url = create_test_base_url();
        
        let playlist = M3u8Playlist::parse(content, base_url).unwrap();
        let debug_str = format!("{playlist:?}");
        
        assert!(!debug_str.is_empty());
        assert!(debug_str.contains("M3u8Playlist"));
    }

    #[test]
    fn test_m3u8_segment_debug() {
        let segment = M3u8Segment::new(
            "test.ts".to_string(),
            5.0,
            1
        );
        let debug_str = format!("{segment:?}");
        
        assert!(!debug_str.is_empty());
        assert!(debug_str.contains("M3u8Segment"));
    }

    #[test]
    fn test_url_join_error_handling() {
        // 测试无效的base_url和相对路径的组合
        let content = "#EXTM3U\n#EXTINF:10.0,\n../invalid/../../segment1.ts";
        let base_url = Url::parse("https://example.com/").unwrap();
        
        // 这个应该仍然能正常处理，因为URL::join能处理大部分情况
        let result = M3u8Playlist::parse(content, base_url);
        // 根据实际的URL::join实现，这可能成功也可能失败
        // 主要是测试不会panic
        if let Ok(playlist) = result {
            assert!(!playlist.segments().is_empty());
        }
        // 错误也是可接受的
    }

    #[test]
    fn test_m3u8_playlist_parse_with_whitespace() {
        let content = "  #EXTM3U  \n  #EXTINF:10.0,  \n  segment1.ts  \n  #EXTINF:5.0,  \n  segment2.ts  ";
        let base_url = create_test_base_url();
        
        let playlist = M3u8Playlist::parse(content, base_url).unwrap();
        let segments = playlist.segments();
        
        assert_eq!(segments.len(), 2);
        assert_eq!(segments[0].duration(), 10.0);
        assert_eq!(segments[1].duration(), 5.0);
    }
}